/*
 * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "sle_callback_adapter.h"
#include "sle_device_discovery.h"
#include "sle_connection_manager.h"
#include "sle_ssap_server.h"
#include "sle_ssap_client.h"

static SleHostHdiCallbacks g_hostCbs;
static SleAnnounceHdiCallbacks g_announceCbs;
static SleSeekHdiCallbacks g_seekCbs;
static SleConnectHdiCallbacks g_connectCbs;
static SleSsapsHdiCallbacks g_ssapsCbs;
static SleSsapcHdiCallbacks g_ssapcCbs;

void on_sle_announce_enable_callback(uint32_t announce_id, errcode_t status)
{
    if(g_announceCbs.OnAnnounceStartCb != NULL) {
        SleHdiErrorCode errCode = (SleHdiErrorCode)status;
        g_announceCbs.OnAnnounceStartCb(announce_id, errCode);
    }
}

void on_sle_announce_disable_callback(uint32_t announce_id, errcode_t status)
{
    if(g_announceCbs.OnAnnounceStopCb != NULL) {
        SleHdiErrorCode errCode = (SleHdiErrorCode)status;
        g_announceCbs.OnAnnounceStopCb(announce_id, errCode);
    }
}

void on_sle_announce_terminal_callback(uint32_t announce_id)
{
    if(g_announceCbs.OnAnnounceTerminalCb != NULL) {
        g_announceCbs.OnAnnounceTerminalCb(announce_id);
    }
}

void on_sle_announce_remove_callback(uint32_t announce_id, errcode_t status)
{
    if(g_announceCbs.OnAnnounceRemoveCb != NULL) {
        SleHdiErrorCode errCode = (SleHdiErrorCode)status;
        g_announceCbs.OnAnnounceRemoveCb(announce_id, errCode);
    }
}

void on_sle_seek_enable_callback(errcode_t status)
{
    if(g_seekCbs.OnSeekStartCb != NULL) {
        SleHdiErrorCode errCode = (SleHdiErrorCode)status;
        g_seekCbs.OnSeekStartCb(errCode);
    }
}

void on_sle_seek_disable_callback(errcode_t status)
{
    if(g_seekCbs.OnSeekStopCb != NULL) {
        SleHdiErrorCode errCode = (SleHdiErrorCode)status;
        g_seekCbs.OnSeekStopCb(errCode);
    }
}

void on_sle_seek_result_callback(sle_seek_result_info_t *seek_result_data)
{
    if (g_seekCbs.OnSeekResultCb == NULL) {
        return;
    }
    SleSeekResult seekResultData = {
        .eventType = seek_result_data->event_type,
        .rssi = seek_result_data->rssi,
        .dataStatus = seek_result_data->data_status,
        .dataLength = seek_result_data->data_length,
        .data = seek_result_data->data,
    };
    seekResultData.addr.addrType = (SleDevAddrType)seek_result_data->addr.type;
    memcpy(seekResultData.addr.addr, seek_result_data->addr.addr, sizeof(seekResultData.addr.addr));
    seekResultData.dirAddr.addrType = (SleDevAddrType)seek_result_data->direct_addr.type;
    memcpy(seekResultData.dirAddr.addr, seek_result_data->direct_addr.addr, sizeof(seekResultData.dirAddr.addr));
    g_seekCbs.OnSeekResultCb(&seekResultData);
}

void on_sle_enable_callback(errcode_t status)
{
    if(g_hostCbs.OnSleEnabledCb != NULL) {
        SleHdiErrorCode errCode = (SleHdiErrorCode)status;
        g_hostCbs.OnSleEnabledCb(errCode);
    }
}

void on_sle_disable_callback(errcode_t status)
{
    if(g_hostCbs.OnSleDisabledCb != NULL) {
        SleHdiErrorCode errCode = (SleHdiErrorCode)status;
        g_hostCbs.OnSleDisabledCb(errCode);
    }
}

static sle_announce_seek_callbacks_t g_sle_announce_seek_callbacks = {
    .sle_enable_cb = on_sle_enable_callback,
    .sle_disable_cb = on_sle_disable_callback,
    .announce_enable_cb = on_sle_announce_enable_callback,
    .announce_disable_cb = on_sle_announce_disable_callback,
    .announce_terminal_cb = on_sle_announce_terminal_callback,
    .announce_remove_cb = on_sle_announce_remove_callback,
    .seek_enable_cb = on_sle_seek_enable_callback,
    .seek_disable_cb = on_sle_seek_disable_callback,
    .seek_result_cb = on_sle_seek_result_callback,
};

void on_sle_connect_state_changed_callback(uint16_t conn_id, const sle_addr_t *addr,
    sle_acb_state_t conn_state, sle_pair_state_t pair_state, sle_disc_reason_t disc_reason)
{
    if(g_connectCbs.OnConnectStateChangedCb) {
        SleAcbState connectState = (SleAcbState)conn_state;
        SlePairState pairState = (SlePairState)pair_state;
        SleDisConnectReason discReason = (SleDisConnectReason)disc_reason;
        SleDeviceAddress addrHdi = {
            .addrType = (SleDevAddrType)addr->type,
        };
        memcpy(addrHdi.addr, addr->addr, sizeof(addrHdi.addr));
        g_connectCbs.OnConnectStateChangedCb(conn_id, &addrHdi, connectState, pairState, discReason);
    }
}

void on_sle_connect_param_update_callback(uint16_t conn_id, errcode_t status,
    const sle_connection_param_update_evt_t *param)
{
    if(g_connectCbs.OnConnectParamUpdateCb) {
        SleConnectionParamUpdateEvt paramHdi;
        memcpy(&paramHdi, param, sizeof(paramHdi));
        g_connectCbs.OnConnectParamUpdateCb(conn_id, (SleHdiErrorCode)status, &paramHdi);
    }
}

void on_sle_connect_param_update_req_callback(uint16_t conn_id, errcode_t status,
    const sle_connection_param_update_req_t *param)
{
    if(g_connectCbs.OnConnectParamUpdateReqCb) {
        SleConnectParam paramHdi;
        memcpy(&paramHdi, param, sizeof(paramHdi));
        g_connectCbs.OnConnectParamUpdateReqCb(conn_id, (SleHdiErrorCode)status, &paramHdi);
    }
}

void on_sle_auth_complete_callback(uint16_t conn_id, const sle_addr_t *addr, errcode_t status,
    const sle_auth_info_evt_t* evt)
{
    if(g_connectCbs.OnAuthCompleteCb) {
        SleDeviceAddress addrHdi = {
            .addrType = (SleDevAddrType)addr->type,
        };
        memcpy(addrHdi.addr, addr->addr, sizeof(addrHdi.addr));
        SleAuthInfo evtHdi = {
            .cryptoAlgo = (SleCryptoAlgo)evt->crypto_algo,
            .keyDerivAlgo = (SleKeyDerivAlgo)evt->key_deriv_algo,
            .integrChkInd = (SleIntegrChkInd)evt->integr_chk_ind,
        };
        memcpy(evtHdi.linkKey, evt->link_key, sizeof(evtHdi.linkKey));
        g_connectCbs.OnAuthCompleteCb(conn_id, &addrHdi, (SleHdiErrorCode)status, &evtHdi);
    }
}

void on_sle_pair_complete_callback(uint16_t conn_id, const sle_addr_t *addr, errcode_t status)
{
    if(g_connectCbs.OnPairCompleteCb) {
        SleDeviceAddress addrHdi = {
            .addrType = (SleDevAddrType)addr->type,
        };
        memcpy(addrHdi.addr, addr->addr, sizeof(addrHdi.addr));
        g_connectCbs.OnPairCompleteCb(conn_id, &addrHdi, (SleHdiErrorCode)status);
    }
}

void on_sle_read_rssi_callback(uint16_t conn_id, int8_t rssi, errcode_t status)
{
    if(g_connectCbs.OnReadRssiCb) {
        g_connectCbs.OnReadRssiCb(conn_id, rssi, (SleHdiErrorCode)status);
    }
}

void on_sle_low_latency_callback(uint8_t status, sle_addr_t *addr, uint8_t rate)
{
    return;
}

void on_sle_set_phy_callback(uint16_t conn_id, errcode_t status, const sle_set_phy_t *param)
{
    if(g_connectCbs.OnSetPhyParamCb) {
        g_connectCbs.OnSetPhyParamCb(conn_id, (SleHdiErrorCode)status, &param);
    }
}

static sle_connection_callbacks_t g_sle_connection_callbacks = {
    .connect_state_changed_cb = on_sle_connect_state_changed_callback,
    .connect_param_update_req_cb = on_sle_connect_param_update_req_callback,
    .connect_param_update_cb = on_sle_connect_param_update_callback,
    .auth_complete_cb = on_sle_auth_complete_callback,
    .pair_complete_cb = on_sle_pair_complete_callback,
    .read_rssi_cb = on_sle_read_rssi_callback,
    .low_latency_cb = on_sle_low_latency_callback,
    .set_phy_cb = on_sle_set_phy_callback,
};

void on_ssaps_start_service_callback(uint8_t server_id, uint16_t handle, errcode_t status)
{
    if(g_ssapsCbs.OnServiceStartedCb) {
        g_ssapsCbs.OnServiceStartedCb(server_id, handle,(SleHdiErrorCode)status);
    }
}

void on_ssaps_delete_all_service_callback(uint8_t server_id, errcode_t status)
{
    if(g_ssapsCbs.OnAllServiceRemovedCb) {
        g_ssapsCbs.OnAllServiceRemovedCb(server_id, (SleHdiErrorCode)status);
    }
}

void on_ssaps_read_request_callback(uint8_t server_id, uint16_t conn_id, ssaps_req_read_cb_t *read_cb_para,
    errcode_t status)
{
    if(g_ssapsCbs.OnRecvReadReqCb) {
        SsapsReqReadCbParam param = {
            .requestId = read_cb_para->request_id,
            .handle = read_cb_para->handle,
            .type = (SsapPropertyType)read_cb_para->type,
            .needRsp = read_cb_para->need_rsp,
            .needAuthorize = read_cb_para->need_authorize,
        };
        g_ssapsCbs.OnRecvReadReqCb(server_id, conn_id, &param, (SleHdiErrorCode)status);
    }
}

void on_ssaps_write_request_callback(uint8_t server_id, uint16_t conn_id, ssaps_req_write_cb_t *write_cb_para,
    errcode_t status)
{
    static uint32_t watchDogWrite = 0;
    if(g_ssapsCbs.OnRecvWriteReqCb) {
        SsapsReqWriteCbPara param = {
            .requestId = write_cb_para->request_id,
            .handle = write_cb_para->handle,
            .type = (SsapPropertyType)write_cb_para->type,
            .needRsp = write_cb_para->need_rsp,
            .needAuthorize = write_cb_para->need_authorize,
            .valueLen = write_cb_para->length,
            .value = write_cb_para->value,
        };
        watchDogWrite++;
        if (watchDogWrite % 50 == 0) {
            (void)uapi_watchdog_kick();
        }
        g_ssapsCbs.OnRecvWriteReqCb(server_id, conn_id, &param, (SleHdiErrorCode)status);
    }
}

void on_ssaps_mtu_changed_callback(uint8_t server_id, uint16_t conn_id,
    ssap_exchange_info_t *info, errcode_t status)
{
    SsapMtuInfo infoCb = {
        .mtuSize = info->mtu_size,
        .version = info->version,
    };
    errcode_t result = sle_set_data_len(conn_id, (uint16_t)info->mtu_size - 50);
    if(result != ERRCODE_SUCC) {
        SLE_LOGE("sle_set_data_len:%d fail(%d)",info->mtu_size - 50, result);
    }
    if(g_ssapsCbs.OnMtuChangedCb) {
        g_ssapsCbs.OnMtuChangedCb(server_id, conn_id, &infoCb, (SleHdiErrorCode)status);
    }
}

static ssaps_callbacks_t g_ssaps_callbacks = {
    .start_service_cb= on_ssaps_start_service_callback,
    .delete_all_service_cb = on_ssaps_delete_all_service_callback,
    .read_request_cb = on_ssaps_read_request_callback,
    .write_request_cb = on_ssaps_write_request_callback,
    .mtu_changed_cb = on_ssaps_mtu_changed_callback,
};

void on_ssapc_find_structure_callback(uint8_t client_id, uint16_t conn_id,
    ssapc_find_service_result_t *service, errcode_t status)
{
    ClientFindServiceRes serviceCb = {
        .startHandle = service->start_hdl,
        .endHandle = service->end_hdl,
    };
    memcpy(&serviceCb.uuid, &service->uuid, sizeof(serviceCb.uuid));
    if(g_ssapcCbs.OnStructureFoundCb) {
        g_ssapcCbs.OnStructureFoundCb(client_id, conn_id, &serviceCb, (SleHdiErrorCode)status);
    }
}

void on_ssapc_find_property_callback(uint8_t client_id, uint16_t conn_id,
    ssapc_find_property_result_t *property, errcode_t status)
{
    if(g_ssapcCbs.OnPropertyFoundCb) {
        ClientFindPropertyRes propertyHdi = {
            .handle = property->handle,
            .operateIndication = (SsapOperateIndication)property->operate_indication,
            .descriptorsCount = property->descriptors_count,
            .descriptorsType = (SsapPropertyDescriptorType)property->descriptors_type[0],
        };
        memcpy(&propertyHdi.uuid, &property->uuid, sizeof(propertyHdi.uuid));
        g_ssapcCbs.OnPropertyFoundCb(client_id, conn_id, &propertyHdi, (SleHdiErrorCode)status);
    }
}

void on_ssapc_find_structure_complete_callback(uint8_t client_id, uint16_t conn_id,
    ssapc_find_structure_result_t *structure_result, errcode_t status)
{
    if(g_ssapcCbs.OnFindStructureCmpCb) {
        ClientFindStructureRes resultHdi = {
            .type = (SsapFindType) structure_result->type,
        };
        memcpy(&(resultHdi.uuid), &(structure_result->uuid), sizeof(resultHdi.uuid));
        g_ssapcCbs.OnFindStructureCmpCb(client_id, conn_id, &resultHdi, (SleHdiErrorCode)status);
    }
}

void on_ssapc_read_cfm_callback(uint8_t client_id, uint16_t conn_id, ssapc_handle_value_t *read_data,
    errcode_t status)
{
    if(g_ssapcCbs.OnRecvReadResponseCb) {
        ClientHandleValue valueHdi = {
            .handle = read_data->handle,
            .type = (SleSsapPropertyType)read_data->type,
            .dataLength = read_data->data_len,
            .data = read_data->data,
        };
        g_ssapcCbs.OnRecvReadResponseCb(client_id, conn_id, &valueHdi, (SleHdiErrorCode)status);
    }
}

void on_ssapc_read_by_uuid_complete_callback(uint8_t client_id, uint16_t conn_id,
    ssapc_read_by_uuid_cmp_result_t *cmp_result, errcode_t status)
{
    if(g_ssapcCbs.OnRreadByUuidCmpCb) {
        ClientReadByUuidCmpRes resultHdi = {
            .type = (SleSsapPropertyType)cmp_result->type,
        };
        memcpy(&(resultHdi.uuid), &(cmp_result->uuid), sizeof(resultHdi.uuid));
        g_ssapcCbs.OnRreadByUuidCmpCb(client_id, conn_id, &resultHdi, (SleHdiErrorCode)status);
    }
}

void on_ssapc_write_cfm_callback(uint8_t client_id, uint16_t conn_id, ssapc_write_result_t *write_result,
    errcode_t status)
{
    if(g_ssapcCbs.OnRecvWriteResponseCb) {
        ClientWriteRes resultHdi = {
            .handle = write_result->handle,
            .type = (SleSsapPropertyType)write_result->type,
        };
        g_ssapcCbs.OnRecvWriteResponseCb(client_id, conn_id, &resultHdi, (SleHdiErrorCode)status);
    }
}

void on_ssapc_exchange_info_callback(uint8_t client_id, uint16_t conn_id, ssap_exchange_info_t *param,
    errcode_t status)
{
    SsapMtuInfo paramCb = {
        .mtuSize = param->mtu_size,
        .version = param->version,
    };
    errcode_t result = sle_set_data_len(conn_id, (uint16_t)param->mtu_size - 50);
    if(result != ERRCODE_SUCC) {
        SLE_LOGE("sle_set_data_len:%d fail(%d)",param->mtu_size - 50, result);
    }
    if(g_ssapcCbs.OnExchangeInfoCb) {
        g_ssapcCbs.OnExchangeInfoCb(client_id, conn_id, &paramCb, (SleHdiErrorCode)status);
    }
}

void on_ssapc_notification_callback(uint8_t client_id, uint16_t conn_id, ssapc_handle_value_t *data,
    errcode_t status)
{
    static uint32_t watchDogNotify = 0;
    if(g_ssapcCbs.OnRecvNotificationCb) {
        ClientHandleValue valueHdi = {
            .handle = data->handle,
            .type = (SleSsapPropertyType)data->type,
            .dataLength = data->data_len,
            .data = data->data,
        };
        watchDogNotify++;
        if (watchDogNotify % 50 == 0) {
            (void)uapi_watchdog_kick();
        }
        g_ssapcCbs.OnRecvNotificationCb(client_id, conn_id, &valueHdi, (SleHdiErrorCode)status);
    }
}

void on_ssapc_indication_callback(uint8_t client_id, uint16_t conn_id, ssapc_handle_value_t *data,
    errcode_t status)
{
    if(g_ssapcCbs.OnRecvIndicationCb) {
        ClientHandleValue valueHdi = {
            .handle = data->handle,
            .type = (SleSsapPropertyType)data->type,
            .dataLength = data->data_len,
            .data = data->data,
        };
        g_ssapcCbs.OnRecvIndicationCb(client_id, conn_id, &valueHdi, (SleHdiErrorCode)status);
    }
}

static ssapc_callbacks_t g_ssapc_callbacks = {
    .find_structure_cb = on_ssapc_find_structure_callback,
    .ssapc_find_property_cbk = on_ssapc_find_property_callback, 
    .find_structure_cmp_cb = on_ssapc_find_structure_complete_callback,
    .read_cfm_cb = on_ssapc_read_cfm_callback,
    .read_by_uuid_cmp_cb = on_ssapc_read_by_uuid_complete_callback,
    .write_cfm_cb = on_ssapc_write_cfm_callback,
    .exchange_info_cb = on_ssapc_exchange_info_callback,
    .notification_cb = on_ssapc_notification_callback,
    .indication_cb = on_ssapc_indication_callback,
};

SleHdiErrorCode InitCallbackAdapter(void)
{
    SLE_LOGD("InitCallbackAdapter");
    SleHdiErrorCode ret = SLE_HDI_ERRCODE_FAIL;
    ret = (SleHdiErrorCode)sle_announce_seek_register_callbacks(&g_sle_announce_seek_callbacks);
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_HDI_ERRCODE_SUCC, ret);
    ret = (SleHdiErrorCode)sle_connection_register_callbacks(&g_sle_connection_callbacks);
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_HDI_ERRCODE_SUCC, ret);
    ret = (SleHdiErrorCode)ssaps_register_callbacks(&g_ssaps_callbacks);
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_HDI_ERRCODE_SUCC, ret);
    ret = (SleHdiErrorCode)ssapc_register_callbacks(&g_ssapc_callbacks);
    return SLE_HDI_ERRCODE_SUCC;
}

SleHdiErrorCode RegisterHostCallbackAdapter(SleHostHdiCallbacks *hostCbs)
{
    SLE_ASSERT_AND_RETURN_RET(hostCbs != NULL, SLE_HDI_ERRCODE_INVALID_PARAM);
    memcpy(&g_hostCbs, hostCbs, sizeof(SleHostHdiCallbacks));
    return SLE_HDI_ERRCODE_SUCC;
}

SleHdiErrorCode RegisterAnnounceCallbackAdapter(SleAnnounceHdiCallbacks *announceCbs)
{
    SLE_ASSERT_AND_RETURN_RET(announceCbs != NULL, SLE_HDI_ERRCODE_INVALID_PARAM);
    memcpy(&g_announceCbs, announceCbs, sizeof(SleAnnounceHdiCallbacks));
    return SLE_HDI_ERRCODE_SUCC;
}

SleHdiErrorCode RegisterSeekCallbackAdapter(SleSeekHdiCallbacks *seekCbs)
{
    SLE_ASSERT_AND_RETURN_RET(seekCbs != NULL, SLE_HDI_ERRCODE_INVALID_PARAM);
    memcpy(&g_seekCbs, seekCbs, sizeof(SleSeekHdiCallbacks));
    return SLE_HDI_ERRCODE_SUCC;
}

SleHdiErrorCode RegisterConnectCallbackAdapter(SleConnectHdiCallbacks *connectCbs)
{
    SLE_ASSERT_AND_RETURN_RET(connectCbs != NULL, SLE_HDI_ERRCODE_INVALID_PARAM);
    memcpy(&g_connectCbs, connectCbs, sizeof(SleConnectHdiCallbacks));
    return SLE_HDI_ERRCODE_SUCC;
}

SleHdiErrorCode RegisterSsapsCallbackAdapter(SleSsapsHdiCallbacks *ssapsCbs)
{
    SLE_ASSERT_AND_RETURN_RET(ssapsCbs != NULL, SLE_HDI_ERRCODE_INVALID_PARAM);
    memcpy(&g_ssapsCbs, ssapsCbs, sizeof(SleSsapsHdiCallbacks));
    return SLE_HDI_ERRCODE_SUCC;
}

SleHdiErrorCode RegisterSsapcCallbackAdapter(SleSsapcHdiCallbacks *ssapcCbs)
{
    SLE_ASSERT_AND_RETURN_RET(ssapcCbs != NULL, SLE_HDI_ERRCODE_INVALID_PARAM);
    memcpy(&g_ssapcCbs, ssapcCbs, sizeof(SleSsapcHdiCallbacks));
    return SLE_HDI_ERRCODE_SUCC;
}